﻿using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

using iStudio.Structure.Extension;
using iStudio.Structure.Engine;

namespace iStudio.Structure.Command
{
    public static class MenuInitializer
    {
        /// <summary>
        /// Populates menu with commands in modules
        /// </summary>
        /// <param name="baseMenu">main menu to fill</param>
        /// <param name="modules">modules with commands</param>
        public static void InitializeMenu (MenuBase baseMenu, IEnumerable<Module> modules)
        {
            InitializeMenu (baseMenu, modules, ModuleCommandTypes.MainMenu);
        }

        /// <summary>
        /// Populates menu with commands in modules
        /// </summary>
        /// <param name="baseMenu">menu to fill</param>
        /// <param name="modules">modules with commands</param>
        /// <param name="type">type of menu to fill</param>
        public static void InitializeMenu(MenuBase baseMenu, IEnumerable<Module> modules, ModuleCommandType type)
        {
            foreach (Module module in modules)
            {
                foreach (ModuleCommand command in module.Commands)
                {
                    if (command.CommandType == type)
                    {
                        HookMenuItem(baseMenu, command, module, type);
                    }
                }
            }
        }

        private static void HookMenuItem (MenuBase baseMenu, ModuleCommand command, Module module, ModuleCommandType type)
        {
            //
            // Get menu commands
            //

            string[] menuNames = command.Text.Split('/');

            //
            // Check if all menus are alpha-numeric, not allowing weird chars
            //

            for (int i = 0; i < menuNames.Length; i++)
            {
                menuNames[i] = BasicString.ParseAlphaNumString(menuNames[i]);
            }

            //
            // Add commands into menu
            //

            for (int i = 0; i < menuNames.Length; i++)
            {
                // If we should search for menu items
                MenuItem itemExists = FindMenuItem(baseMenu.Items, menuNames, i);

                // If the item is not there, we add it
                if (itemExists == null)
                {
                    MenuItem item = new MenuItem
                    {
                        Header = menuNames[i],
                    };

                    if (i == (menuNames.Length - 1))
                    {
                        item.Command = command;
                        item.CommandParameter = command.Parameter;

                        CommandBinding commandBinding = new CommandBinding(command, module.ExecutedCommand);

                        item.CommandBindings.Add(commandBinding);
                    }

                    //
                    // Find parent of the item, we need to insert
                    //

                    if (i > 0)
                    {
                        MenuItem parent = FindMenuItem(baseMenu.Items, menuNames, i - 1);
                        parent.Items.Add(item);
                    }
                    //
                    // Add to main menu, if we have no item there yet
                    //
                    else
                    {
                        //
                        // Find position for the item to insert
                        //

                        int insertIndex = 0;
                        int skipCount = 0;

                        // Start after edit -> only in main menu
                        if (type == ModuleCommandTypes.MainMenu)
                        {
                            insertIndex = 2;
                            skipCount = 2;
                        }

                        while (insertIndex < baseMenu.Items.Count - skipCount)
                        {
                            string menuItemHeader = (string)((MenuItem)baseMenu.Items[insertIndex]).Header;

                            // If the menu item has first letter higher than first letter of inserting item
                            if (menuItemHeader[0] > command.Text[0])
                            {
                                break;
                            }

                            insertIndex++;
                        }

                        baseMenu.Items.Insert(insertIndex, item);
                    }
                }
            }
        }

        private static MenuItem FindMenuItem(ItemCollection items, string[] menu, int index)
        {
            int searchPos = -1;

            while (searchPos != index)
            {
                bool found = false;

                for (int i = 0; i < items.Count; i++)
                {
                    var item = items[i] as MenuItem;

                    if (item != null)
                    {
                        if ((string)item.Header == BasicString.ImplodeString(menu, '/', searchPos + 2))
                        {
                            items = item.Items;

                            searchPos++;

                            found = true;

                            if (searchPos == index)
                            {
                                return item;
                            }

                            break;
                        }
                    }
                }

                if (found == false)
                {
                    break;
                }
            }

            return null;
        }
    }
}
